package no.ntnu.fp.gui;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import javax.swing.SwingUtilities;

import no.ntnu.fp.gui.events.GuiEvent;
import no.ntnu.fp.gui.listeners.GuiListener;
import no.ntnu.fp.model.events.ModelEvent;
import no.ntnu.fp.model.listeners.ModelListener;

public class EventDispatcher {
	private final static Map<Class<? extends GuiEvent>, GuiListener<?>> 
	guiListeners = new HashMap<Class<? extends GuiEvent>, GuiListener<?>>();
	
	private final static Map<Class<? extends ModelEvent>, ModelListener<?>> 
	modelListeners = new HashMap<Class<? extends ModelEvent>, ModelListener<?>>();
	
	private final static Executor longRunningExecutor 
								= Executors.newSingleThreadExecutor();

	public static void registerGuiListeners(Map<Class<? extends GuiEvent>, 
			GuiListener<?>> map) {
		guiListeners.putAll(map);
	}
	
	public static void registerModelListeners(Map<Class<? extends ModelEvent>, 
			ModelListener<?>> map) {
		modelListeners.putAll(map);
	}
	
	public static <T extends GuiEvent> void dispatchEvent(final T evt) {
		GuiListener<?> listener = guiListeners.get(evt.getClass());
		if (listener == null) {
			throw new UnsupportedOperationException("Handler of " + 
					evt.getClass().getSimpleName() + " is not registered");
		}
		@SuppressWarnings("unchecked")
		final GuiListener<T> l = (GuiListener<T>) listener;
		if (l.isLongRunningListener()) {
			Runnable task = new Runnable() {
				@Override
				public void run() {
					l.handleEvent(evt);
				}
			};
			longRunningExecutor.execute(task);
			
		} else {
			l.handleEvent(evt);
		}
	}
	
	public static <T extends ModelEvent> void dispatchEvent(final T evt) {
		ModelListener<?> listener = modelListeners.get(evt.getClass());
		if (listener == null) {
			throw new UnsupportedOperationException("Handler of " + 
					evt.getClass().getSimpleName() + " is not registered");
		}
		@SuppressWarnings("unchecked")
		final ModelListener<T> l = (ModelListener<T>)listener;
		if (SwingUtilities.isEventDispatchThread()) {
			l.handleEvent(evt);
		} else {
			SwingUtilities.invokeLater(new Runnable() {
				@Override
				public void run() {
					l.handleEvent(evt);
				}
			});
		}
	}
}
